package zone

import (
	"fmt"
	"net/url"

	"github.com/cayleygraph/cayley"
	"github.com/cayleygraph/cayley/graph"
	_ "github.com/cayleygraph/cayley/graph/kv/bolt"
	_ "github.com/cayleygraph/cayley/graph/memstore"
	"github.com/cayleygraph/cayley/quad"
	"github.com/cayleygraph/cayley/schema"
	"github.com/pkg/errors"
)

// 负责总控处理
// 1。初始化战区：注册节点，注册势力
// 2。查询单个节点状态

// 负责以图方式存储

const (
	Link     = "link"
	Master   = "master"
	typeIRI  = quad.IRI("rdf:type")
	powerIRI = quad.IRI("ex:Power")
	nodeIRI  = quad.IRI("ex:Node")
)

func ParseDsn(dsn string) (string, string, error) {
	u, err := url.Parse(dsn)
	if err != nil {
		return "", "", err
	}
	switch u.Scheme {
	case "mysql":
		return "mysql", MysqlPath(u), nil
	case "memstore":
		return "memstore", "", nil
	case "bolt":
		return "bolt", u.Path, nil
	}
	return "", "", errors.New("Schema not support, only mysql")
}
func MysqlPath(u *url.URL) string {
	return fmt.Sprintf("%s@tcp(%s)%s?charset=utf8", u.User, u.Host, u.Path)
}

type Layout struct {
	id    string
	name  string
	path  string
	store *cayley.Handle
	sch   *schema.Config
	//tx         *graph.Transaction
	dispatcher *dispatcher
}

func NewLayout(id string, dsn string) (*Layout, error) {
	name, dbpath, err := ParseDsn(dsn)
	if err != nil {
		return nil, err
	}

	if dbpath != "" {
		if err := graph.InitQuadStore(name, dbpath, nil); err != nil {
			return nil, err
		}
	}

	store, err := cayley.NewGraph(name, dbpath, nil)
	if err != nil {
		return nil, err
	}

	sch := schema.NewConfig()
	sch.Label = quad.StringToValue(id)
	schema.RegisterType(nodeIRI, Node{})
	schema.RegisterType(powerIRI, Power{})

	layout := &Layout{
		id:         id,
		name:       name,
		path:       dbpath,
		sch:        sch,
		store:      store,
		dispatcher: newDispatcher(),
	}

	return layout, nil
}

func (layout *Layout) Start(dispatch Dispatch) error {
	return layout.dispatcher.Attach(dispatch, layout)
}

func (layout *Layout) Close() {
	layout.dispatcher.Close()
}

func (layout *Layout) IdWrap(id string) quad.IRI {
	return quad.IRI(layout.id + "/" + id)
}

func (layout *Layout) Transaction(callback func(tx *graph.Transaction) error) error {
	tx := cayley.NewTransaction()
	err := callback(tx)
	if err != nil {
		return err
	}
	return layout.store.ApplyTransaction(tx)
}

func (layout *Layout) Path(id string) *cayley.Path {
	return cayley.StartPath(layout.store, layout.IdWrap(id)).LabelContext(layout.sch.Label)
}
func (layout *Layout) PathIRI(id quad.IRI) *cayley.Path {
	return cayley.StartPath(layout.store, id)
}

func (layout *Layout) RegisterPower(tx *graph.Transaction, id string, master string) *Power {
	qw := graph.NewTxWriter(tx, graph.Add)
	iri := layout.IdWrap(id)
	if _, err := layout.UpdatePower(qw, &Power{ID: iri}); err != nil {
		fmt.Println(err)
	}
	if master != "" {
		if err := qw.WriteQuad(quad.Make(iri, Master, layout.IdWrap(master), layout.sch.Label)); err != nil {
			fmt.Println(err)
		}
	}
	return layout.Power(id)
}

func (layout *Layout) UpdatePower(qw quad.Writer, power *Power) (quad.Value, error) {
	return layout.sch.WriteAsQuads(qw, *power)
}

func (layout *Layout) RegisterNode(tx *graph.Transaction, id string, links []string) *Node {
	qw := graph.NewTxWriter(tx, graph.Add)
	iri := layout.IdWrap(id)
	if _, err := layout.UpdateNode(qw, &Node{ID: iri}); err != nil {
		fmt.Println(err)
	}
	for _, nid := range links {
		if err := qw.WriteQuad(quad.Make(iri, Link, layout.IdWrap(nid), layout.sch.Label)); err != nil {
			fmt.Println(err)
		}
	}

	return layout.Node(id)
}

func (layout *Layout) UpdateNode(qw quad.Writer, node *Node) (quad.Value, error) {
	return layout.sch.WriteAsQuads(qw, *node)
}

func (layout *Layout) Init(callback func(tx *graph.Transaction) error) error {
	return layout.Transaction(callback)
}

func (layout *Layout) Node(id string) *Node {
	return layout.NodeIRI(layout.IdWrap(id))
}

func (layout *Layout) NodeIRI(id quad.IRI) *Node {
	var n Node
	if err := layout.sch.LoadTo(layout.dispatcher.context, layout.store, &n, id); err != nil {
		fmt.Println(err)
	}
	n.layout = layout
	return &n
}

func (layout *Layout) Nodes() []Node {
	return layout.NodesWithPath(cayley.StartPath(layout.store, nodeIRI).LabelContext(layout.sch.Label).In(typeIRI))
}

func (layout *Layout) NodesWithPath(path *cayley.Path) []Node {
	var nodes []Node
	if err := layout.sch.LoadPathTo(layout.dispatcher.context, layout.store, &nodes, path); err != nil {
		fmt.Println(err)
	}
	for i := 0; i < len(nodes); i++ {
		nodes[i].layout = layout
	}
	return nodes
}

func (layout *Layout) Power(id string) *Power {
	return layout.PowerIRI(layout.IdWrap(id))
}

func (layout *Layout) PowerIRI(id quad.IRI) *Power {
	//it := cayley.StartPath(layout.store, id).Out().Has(typeIRI, nodeIRI).Iterate(layout.dispatch.Context())
	////fmt.Println(it.Count())
	//it.Each(func(value graph.Value) {
	//	fmt.Println(value)
	//	a := layout.store.NameOf(value)
	//	nativeValue := quad.NativeOf(a)
	//	fmt.Println(nativeValue)
	//})
	var p Power
	if err := layout.sch.LoadTo(layout.dispatcher.context, layout.store, &p, id); err != nil {
		fmt.Println(err)
	}
	p.layout = layout
	return &p
}

func (layout *Layout) Powers() []Power {
	return layout.PowersWithPath(cayley.StartPath(layout.store, powerIRI).LabelContext(layout.sch.Label).In(typeIRI))
}

func (layout *Layout) PowersWithPath(path *cayley.Path) []Power {
	var powers []Power
	if err := layout.sch.LoadPathTo(layout.dispatcher.context, layout.store, &powers, path); err != nil {
		fmt.Println(err)
	}
	for i := 0; i < len(powers); i++ {
		powers[i].layout = layout
	}
	return powers
}
